package cur.channel.server.netty;

import com.alibaba.fastjson.JSON;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import lombok.Data;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * Created by gantianxing on 2017/7/20.
 */
@Component
public class NettyManager {
    private static final Log log = LogFactory.getLog(NettyManager.class);

    @Resource
    private NettyProperties nettyProperties;
    @Resource
    private NettyServerHandler businessHander;
    private ChannelFuture channelFuture ;
    private ServerBootstrap bootstrap ;
    private EventLoopGroup boss;
    private EventLoopGroup worker;
    //25秒没有接收到客户端请求，客户端可能已经挂掉。删除该客户端连接


    /**
     * netty服务启动方法
     */
    @PostConstruct
    public void init(){
        this.boss=new NioEventLoopGroup();
        this.worker=new NioEventLoopGroup();
        bootstrap=new ServerBootstrap();
        this.startServer();
    }

    /**
     * netty启动服务
     */
    public void startServer() {
        try {
            bootstrap.group(boss,worker);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.option(ChannelOption.SO_BACKLOG, 128);
            //通过NoDelay禁用Nagle,使消息立即发出去，不用等待到一定的数据量才发出去
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            //保持长连接状态
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.childHandler(new ChildChannelHandler(nettyProperties,businessHander) );
            channelFuture = bootstrap.bind(nettyProperties.getPort()).sync();
            if(channelFuture.isSuccess()){
                log.info("netty controller start on port:"+nettyProperties.getPort()+" ---------------");
            }
        } catch (InterruptedException e) {
            log.error("netty controller start failed",e);
        }

    }

    /**
     * netty服务销毁方法
     */
    @PreDestroy
    public void destroy(){
        if(channelFuture!=null){
            Channel channel = channelFuture.channel();
            if(channel!=null && channel.isOpen()){
                channel.close();
            }
        }
        if(boss!=null){
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
        log.info("netty controller stop");
    }

    @Data
    public static class NettyEntity {
//        private static final Log log = LogFactory.getLog(NettyClient.class);
        public SocketChannel channel;
        private Integer groupId;
        private String clientId;
//        public NettyEntity(SocketChannel channel){
//            this.channel = channel;
//        }

        public NettyEntity(SocketChannel channel, Integer groupId, String clientId) {
            this.channel = channel;
            this.groupId = groupId;
            this.clientId = clientId;
        }
    }

    private static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
        private NettyProperties nettyProperties;
        private NettyServerHandler businessHander;
        public ChildChannelHandler(NettyProperties nettyProperties, NettyServerHandler businessHander) {
            super();
            this.nettyProperties = nettyProperties;
            this.businessHander = businessHander;
        }
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
//            socketChannel.pipeline().addLast(new ObjectEncoder());
//            socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
            socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
            socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
            socketChannel.pipeline().addLast(new IdleStateHandler(nettyProperties.getReadWaitSecond(), nettyProperties.getWriteWaitSecond(), 0, TimeUnit.SECONDS));
            socketChannel.pipeline().addLast(businessHander);
        }
    }

//    private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
//        private SimpleChannelInboundHandler businessHander;
//
//        public ChildChannelHandler(SimpleChannelInboundHandler businessHander){
//            this.businessHander = businessHander;
//        }
//
//        @Override
//        protected void initChannel(SocketChannel socketChannel) throws Exception {
//            ChannelPipeline pipeline = socketChannel.pipeline();
//            //读监听
//            pipeline.addLast(new IdleStateHandler(READ_WAIT_SECONDS, 0, 0));
//            /*
//             * 使用ObjectDecoder和ObjectEncoder
//             * 因为双向都有写数据和读数据，所以这里需要两个都设置
//             * 如果只读，那么只需要ObjectDecoder即可
//             */
////            pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
////            pipeline.addLast(new ObjectEncoder());
//
//            socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
//            socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
//            pipeline.addLast(businessHander);
//        }
//    }
}
